package com.rs.service.impl;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.rs.config.PadConfig;
import com.rs.data.entity.CoreResrcFile;
import com.rs.data.model.PadForm;
import com.rs.data.model.PadPushData;
import com.rs.data.model.PadReceiverInfo;
import com.rs.mapper.CommonSearchMapper;
import com.rs.service.IPadExchangeHelpService;
import com.rs.utils.*;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.stream.Collectors;

@SuppressWarnings("all")
@Service("padExchangeHelpService")
public class PadExchangeHelpServiceImpl implements IPadExchangeHelpService {

    private static final Logger logger = LoggerFactory.getLogger(PadExchangeHelpServiceImpl.class);

    // 文件保存目录
    @Value("${dict.rootPath}")
    private String rootPath;
    // 卫士通地址
    @Value("${wst.westUrl}")
    private String westUrl;
    // 应用标识
    @Value("${wst.appId}")
    private String appId;
    // 发送者id
    @Value("${wst.sendId}")
    private String sendId;
    // 发送者名称
    @Value("${wst.sendName}")
    private String sendName;
    // 发送者接入应用id
    @Value("${wst.senderAccessAppId}")
    private String senderAccessAppId;
    // 发送者接入应用名称
    @Value("${wst.senderAccessAppName}")
    private String senderAccessAppName;
    // 接收者id
    @Value("${wst.receiverId}")
    private String receiverId;
    // 接收者名称
    @Value("${wst.receiverName}")
    private String receiverName;
    // 接收者接入应用id
    @Value("${wst.receiverAccessAppId}")
    private String receiverAccessAppId;
    // 接收者接入应用名
    @Value("${wst.receiverAccessAppName}")
    private String receiverAccessAppName;

    // 队列
    private static BlockingQueue<PadPushData> queue = new LinkedBlockingQueue<PadPushData>();

    // 缓存线程池
    private static ExecutorService threadPool = Executors.newFixedThreadPool(3);

    // 任务数
    private static int taskSize = 6;

    // 线程是否已启动
    boolean isStartThread = false;

    // 用来启动或停止线程
    static boolean run = true;

    public static boolean isRun() {
        return run;
    }

    public static void setRun(boolean run) {
        PadExchangeHelpServiceImpl.run = run;
    }

    @Resource
    private CommonSearchMapper commonSearchMapper;

    @Override
    public void addData(PadPushData model) {
        ThreadPoolUtil.getExecutorService().execute(() -> pushData(model));
    }

    private void sendData() {
        try {
            while (run) {
                while (queue.size() != 0) {
                    PadPushData pd = queue.take();
                    pushData(pd);
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void pushData(PadPushData model) {
        logger.info("=======================数据推送开始================modul:" + model.getModul() + "============UUID:" + model.getId());
        if (!StringUtils.hasText(model.getId())) {
            logger.info("推送文件id为空", model.getId(), "数据推送", "0", "", "");
            return;
        }
        // 任务id  不能重复 最好随机生成uuid
        if (!StringUtils.hasText(model.getTransId())) {
            model.setTransId(IdUtil.fastSimpleUUID());
        }
        // 新增/附件
        if (PadConfig.ACTION_ADD.equals(model.getAction()) || PadConfig.ACTION_ATTACHMENT.equals(model.getAction())) {
            pushData(model.getId(), model.getModul(), model.getTransId(), model.getAction(), model.getData());
            return;
        }
        // 删除
        if (PadConfig.ACTION_REMOVE.equals(model.getAction())) {
        }
    }

    private void pushData(String uuid, String modul, String formId, String action, String data) {
        List<CoreResrcFile> list = new ArrayList<CoreResrcFile>();
        CoreResrcFile resrcFile = new CoreResrcFile();
        resrcFile.setCrfileUuid(formId);
        if (PadConfig.ACTION_ADD.equals(action)) {
            // 创建表单文件（内容为json数据）
            String filePath = this.createTxtFile(uuid, modul, formId, action, data);
            resrcFile.setCrfileServerPath(filePath);
            // 附件类型只能有0和1，且1类型正文只能有一个
            resrcFile.setType("1");
            list.add(resrcFile);

            // 数据推送
            doPush(uuid, formId, list, modul);
            logger.info("=======================数据推送================modul:" + modul + "============UUID:" + uuid);
        } else if (PadConfig.ACTION_ATTACHMENT.equals(action)) {
            // 获取附件完整路径
            String filePath = data;
            logger.info("=======================获取附件完整路径================fullPath:" + filePath + "============");
            // 处理附件
            resrcFile.setCrfileServerPath(filePath);
            resrcFile.setType("0");
            list.add(resrcFile);

            // 获取文件保存路径
            if (filePath.contains("\\")) {
                filePath = filePath.substring(0, filePath.lastIndexOf("\\"));
            } else {
                filePath = filePath.substring(0, filePath.lastIndexOf("/"));
            }

            // 附件推送 将源文件路径推送过去
            doPush(uuid, formId, list, filePath);
            logger.info("=======================附件推送================modul:" + modul + "============UUID:" + uuid);
        }
    }


    /**
     * 生成模块相关数据txt文件
     */
    private String createTxtFile(String uuid, String modul, String formId, String action, String data) {
        Map<String, Object> map = new HashMap<>();
        map.put("model", data);
        map.put("modulType", modul);
        map.put("formId", formId);
        map.put("action", action);
        return createJsonFile(uuid, map, formId, modul);
    }

    /**
     * 生成json文件返回文件路径
     */
    private String createJsonFile(String uuid, Map<String, Object> map,
                                  String formId, String modul) {
        String jsonString = JSONObject.toJSONString(map);
        String path = File.separator + PadConfig.PAD_FILE_FOLDER_SEND + File.separator +
                PadConfig.FORMAT_SDF_8.format(new Date()) + File.separator;
        File fileFolder = new File(rootPath + path);
        if (!fileFolder.exists()) {
            fileFolder.mkdirs();
        }
        String formFilePath = rootPath + path + separator + formId + PadConfig.FILE_EXT;
        RandomAccessFile raf = null;
        try {
            byte[] bytes = jsonString.getBytes();
            raf = new RandomAccessFile(new File(formFilePath), "rw");
            if (raf != null) {
                raf.write(bytes);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (raf != null) {
                try {
                    raf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return formFilePath;
    }

    private void doPush(String uuid, String formId, List<CoreResrcFile> list, String modul) {
        if (StrUtil.isEmpty(westUrl)) {
            logger.info("=====================卫士通推送地址未配置======================");
            return;
        }
        doPushForm(uuid, 1, formId, list, westUrl, modul); //传输开始
        doPushFile(uuid, formId, list, westUrl, modul);
        doPushForm(uuid, 2, formId, list, westUrl, modul); // 传输结束
    }

    /**
     * 推送表单
     *
     * @param model
     * @param formType 1开始传输 2结束传输
     * @param formId   传输任务的唯一ID
     * @param attLists 附件列表
     * @param url      地址
     * @param modul    模块类型
     */
    private void doPushForm(String uuid, int formType, String formId, List<CoreResrcFile> list, String url, String modul) {
        logger.info("====doPushForm=====表单数据推送=====modul:" + modul + "=====UUID:" + uuid + "=====formType:" + formType + "=====formId:" + formId);

        String formXML = getFormXml(list, formType);
        String formStr = Base64.encode(formXML, "UTF-8");
        StringBuffer xml = new StringBuffer();
        xml.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
        xml.append("<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:gw=\"urn:gwService\">");
        xml.append("<SOAP-ENV:Body>");
        xml.append("<gw:ldtpFormSend>");
        xml.append(XMLParserUtil.getXmlStr("transId", formId));
        xml.append(XMLParserUtil.getXmlStr("appId", appId));
        xml.append(XMLParserUtil.getXmlStr("formType", String.valueOf(formType)));
        xml.append(XMLParserUtil.getXmlStr("form", formStr));
        xml.append(XMLParserUtil.getXmlStr("ext1", modul));
        xml.append("</gw:ldtpFormSend>");
        xml.append("</SOAP-ENV:Body>");
        xml.append("</SOAP-ENV:Envelope>");
        HashMap<String, String> StringHashMap = new HashMap<>();
        StringHashMap.put("Content-type", "text/xml;charset=utf-8");
        try {
            String result = HttpPostUtil.httpPost(url, xml.toString(), StringHashMap, "POST");
        } catch (Exception e) {
            e.printStackTrace();
        }

        logger.info("====doPushForm=====表单数据推送=====modul:" + modul + "=====UUID:" + uuid + "=====formType:" + formType + "=====formId:" + formId);
    }

    private String getFormXml(List list, int formType) {
        PadForm form = new PadForm();
        form.setVersion("V1.0.0"); // 版本
        form.setTitle("数据推送");
        form.setSenderId(sendId);
        form.setSenderName(sendName);
        form.setSenderAccessAppId(senderAccessAppId);
        form.setSenderAccessAppName(senderAccessAppName);
        form.setSecLevel(SecLevel.LEVEL_FIVE.getCode()); // 密级
        form.setPriority(Priority.PRI_02.getCode()); // 优先级
        form.setExchMode("");
        form.setExt("");

        PadReceiverInfo info = new PadReceiverInfo();
        info.setReceiverId(receiverId);
        info.setReceiverName(receiverName);
        info.setReceiverAccessAppId(receiverAccessAppId);
        info.setReceiverAccessAppName(receiverAccessAppName);
        List<PadReceiverInfo> infoList = new ArrayList<PadReceiverInfo>();
        infoList.add(info);
        form.setReceivers(new PadForm.Receivers(infoList));
        form.setFiles(doInitFiles(list, formType));
        return XMLParserUtil.marshal(form);
    }

    private PadForm.Files doInitFiles(List<CoreResrcFile> list, int formType) {
        PadForm.Files files = new PadForm.Files();
        int fileSize = BLOCK_FILE_SIZE;
        files.setFragmentSize(fileSize);
        files.setTotalNum(String.valueOf(list.size()));
        for (CoreResrcFile bean : list) {
            String sourcePath = "0".equals(bean.getType()) ? bean.getCrfileServerPath() :
                    (bean.getCrfileServerPath().indexOf("RESOURCE_ROOT") > -1 ? bean.getCrfileServerPath() : (rootPath + bean.getCrfileServerPath()));
            File file = new File(sourcePath);

            files.getFileInfo().add(new PadForm.FileInfo(Integer.valueOf(bean.getType()),
                    file.length(), file.getName(), CommonUtil.getMD5(file)));
            fileSize += file.length();

            //这里根据需求是否删除 此处由于一条数据一个文件可能会产生比较多文件才删除
            // 传输结束 && 卫士通数据传输txt文件
            if (2 == formType && sourcePath.startsWith(rootPath + File.separator + PadConfig.PAD_FILE_FOLDER_SEND)) {
                // 删除文件
                file.delete();
            }
        }
        return files;
    }

    /**
     * 推送文件
     */
    private void doPushFile(String uuid, String formId, List<CoreResrcFile> list, String url, String modul) {
        logger.info("====doPushFile=====文件数据推送=====modul:" + modul + "=====UUID:" + uuid + "=====formId:" + formId);

        for (CoreResrcFile bean : list) {
            if (bean == null) {
                continue;
            }

            String fileId = bean.getCrfileUuid();
            String fileName = bean.getCrfileName();
            String sourcePath = "0".equals(bean.getType()) ? bean.getCrfileServerPath() :
                    (bean.getCrfileServerPath().indexOf("RESOURCE_ROOT") > -1 ? bean.getCrfileServerPath() : (rootPath + bean.getCrfileServerPath()));

            if (StringUtils.hasText(sourcePath)) {
                File tempFile = new File(sourcePath);
                byte[] fileByte = FileUtil.readBytes(tempFile);
                int chunkFileNum = (int) Math.ceil(fileByte.length * 1.0 / BLOCK_FILE_SIZE);
                int num = 0;
                byte[] bytes = null;
                for (int j = 0; j < chunkFileNum; j++) {
                    if (j == (chunkFileNum - 1)) {
                        // 最后一个包
                        bytes = CommonUtil.subBytes(fileByte, num, fileByte.length - num);
                    } else {
                        bytes = CommonUtil.subBytes(fileByte, num, BLOCK_FILE_SIZE);
                    }
                    sendSlice(uuid, formId, bytes, j + 1, bean.getType(), tempFile.getName(), fileId, url, modul);

                    num += BLOCK_FILE_SIZE;
                }
            } else {
                logger.info(formId + "doPushFile文件推送异常", modul, "文件推送", "0", "", "", "obj转附件实体未匹配");
            }
        }

        logger.info("====doPushFile=====文件数据推送=====modul:" + modul + "=====UUID:" + uuid + "=====formId:" + formId);
    }

    /***
     * 分片推送数据
     *
     * @param model
     * @param bytes 字节数据；发送需要BASE64编码
     * @param index 分片序号。从1开始，按照分片顺序依次累加
     * @param fileType 文件类型，0:附件1:正文
     * @param fileName 文件名称，不带路径。不能为空，长度不超过256字节，一次传输任务中，同样类型文件（正文、附件）的文件名称不能相同
     * @param formId from表单ID;传输任务号，长度不超过32字节;保证其全局唯一
     * @param fileId 传输的文件ID
     * @param url 传输地址
     * @param modul 模块类型
     */
    private void sendSlice(String uuid, String formId, byte[] bytes,
                           int index, String fileType, String fileName, String fileId,
                           String url, String modul) {
        //获取附件的Base64 编码格式的附件
        String encodeFileStr = java.util.Base64.getEncoder().encodeToString(bytes);
        String fileNameSS = "";
        try {
            fileNameSS = new String(fileName.getBytes("UTF-8"), "iso-8859-1");
        } catch (Exception e) {
            e.printStackTrace();
        }

        StringBuffer xml = new StringBuffer();
        xml.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
        xml.append("<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:gw=\"urn:gwService\">");
        xml.append("<SOAP-ENV:Body>");
        xml.append("<gw:ldtpFileSend>");
        xml.append(XMLParserUtil.getXmlStr("transId", formId));
        xml.append(XMLParserUtil.getXmlStr("fileType", fileType));
        xml.append(XMLParserUtil.getXmlStr("fileName", fileNameSS));
        xml.append(XMLParserUtil.getXmlStr("file", encodeFileStr));
        xml.append(XMLParserUtil.getXmlStr("fragId", String.valueOf(index)));
        xml.append(XMLParserUtil.getXmlStr("ext1", modul));
        xml.append("</gw:ldtpFileSend>");
        xml.append("</SOAP-ENV:Body>");
        xml.append("</SOAP-ENV:Envelope>");
        HashMap<String, String> stringStringHashMap = new HashMap<>();
        stringStringHashMap.put("Content-type", "text/xml;charset=utf-8");
        try {
            String result = HttpPostUtil.httpPost(url, xml.toString(), stringStringHashMap, "POST");
            logger.info(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void doSaveLdtpFileSend(String transId, String fileType, String fileName, String file, int fragId, String ext1) {
        byte[] bs = java.util.Base64.getDecoder().decode(file);
        try {
            bs = new String(bs).getBytes("UTF-8");
            doSaveLdtpFileSend(transId, fileType, fileName, bs, fragId, ext1);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void doSaveLdtpFileSend(String transId, String fileType, String fileName, byte[] bs, int fragId, String ext1) {
        RandomAccessFile raf = null;
        try {
            String tempFilePath;
            if ("0".equals(fileType)) {
                tempFilePath = ext1 + separator;
            } else {
                tempFilePath = rootPath + separator + PadConfig.PAD_FILE_FOLDER_RECEIVE + separator +
                        PadConfig.FORMAT_SDF_8.format(new Date()) + separator;
            }
            File fileDir = new File(tempFilePath);
            if (!fileDir.exists()) {
                //创建文件夹
                fileDir.mkdirs();
            }
            tempFilePath = tempFilePath + fileName;
            File tempFile = new File(tempFilePath);
            if (tempFile.exists()) {
                //存在，分片传输，继续写入
                raf = new RandomAccessFile(tempFile, "rw");
                if (raf != null) {
                    raf.write(bs);
                }
            } else {
                // 文件不存在的时候、写入文件
                raf = new RandomAccessFile(tempFile, "rw");
                if (raf != null) {
                    raf.write(bs);
                }
            }
            if (fileName.indexOf("_risenPad") > -1) {
                return;
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (raf != null) {
                try {
                    raf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void doParseLdtpFormSend(String transId) throws Exception {
        String path = separator + PadConfig.PAD_FILE_FOLDER_RECEIVE + separator +
                PadConfig.FORMAT_SDF_8.format(new Date()) + separator;
        String filePath = rootPath + path + separator + transId + PadConfig.FILE_EXT;

        File file = new File(filePath);
        if (!file.exists()) {
            return;
        }
        // 文件转对象
        JSONObject json = JSONObject.parseObject((String) FileUtils.readFileToString(file));
        logger.info("获取到文件转对象：" + json.toJSONString());
        // 获取model
        JSONObject model = json.getJSONObject("model");
        boolean res = true;
        if (PadConfig.ACTION_ADD.equals(json.getString("action"))) {
            // 新增
            res = doParseLdtpFormSave(model);
        } else if (PadConfig.ACTION_REMOVE.equals(json.getString("action"))) {
            // 删除
        }
        // 删除文件
        if (res) {
            file.delete();
        }
    }

    @Resource
    private JdbcTemplateUtil jdbcTemplateUtil;

    /**
     * 接收表单数据保存
     * @param model model
     */
    private boolean doParseLdtpFormSave(JSONObject model) {
        logger.info("接收者接收到数据：" + model.toJSONString());
        // 保存数据操作
        JSONObject tableSchema = model.getJSONObject("tableSchema");
        // 获取表名
        String tableName = tableSchema.getString("tableName");
        Integer res = commonSearchMapper.selectCountByTableName(tableName);
        if (res == 0) {
            logger.info("表[" + tableName + "]在数据库中不存在，请先创建表");
            return false;
        }
        // 附件处理判断
        Optional.ofNullable(tableSchema.getString("hasFile")).ifPresent(hasFile -> {
            if ("true".equals(hasFile)) {
                String path = tableSchema.getString("path");
                model.put(path, tableSchema.getString("prefix") + model.getString(path));
            }
        });
        model.remove("tableSchema");
        // 主键标识处理
        Optional.ofNullable(tableSchema.getString("primaryKey")).ifPresent(primaryKey -> {
            String primaryKeyVal = model.getString(primaryKey);
            Optional.ofNullable(primaryKeyVal).ifPresent(val -> {
                // 删除对应数据
                jdbcTemplateUtil.deleteDataByPrimaryKey(tableName, primaryKey, primaryKeyVal);
            });
        });

        Map<String, Object> columMap = null;
        Map<String, String> columnTypeMap = null;
        // 字段名-字段值映射
        columMap = JSONObject.toJavaObject(model, Map.class);
        // 过滤掉所有为空数据
        columMap = columMap.entrySet().stream().filter(entry -> ObjectUtil.isNotEmpty(entry.getValue())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
        // 字段名-字段flink类型
        columnTypeMap = JSONObject.toJavaObject(tableSchema.getJSONObject("tableColumn"), Map.class);

        // 保存
        return jdbcTemplateUtil.saveDataToDm(tableSchema.getString("tableName"), columMap, columnTypeMap);
    }
}
